#include "serialform.h"
#include "ui_serialform.h"
#include "serialthread.h"

#include <QScreen>
#include <QDebug>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QMessageBox>
#include <QFile>
#include <QFileDialog>
#include <QThread>
#include <QDateTime>

#include "pcpupdateptl.h"
#include  "qcustomplot.h"
#include "shelldialog.h"

#define TIMEMS          qPrintable(QTime::currentTime().toString("HH:mm:ss zzz"))
#define TIME            qPrintable(QTime::currentTime().toString("HH:mm:ss"))
#define QDATE           qPrintable(QDate::currentDate().toString("yyyy-MM-dd"))
#define QTIME           qPrintable(QTime::currentTime().toString("HH-mm-ss"))
#define DATETIME        qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"))
#define STRDATETIME     qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss"))
#define STRDATETIMEMS   qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss-zzz"))

#define MyDATETIMEMS    qPrintable(QDateTime::currentDateTime().toString("MM-dd HH:mm:ss zzz"))

SerialForm::SerialForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SerialForm)
{
    ui->setupUi(this);
    ui->updateStopBtn->setEnabled(false);

    connect(this, &SerialForm::SignalSerialOpen,
            SerialThread::Instance(), &SerialThread::SlotSerialOpen, Qt::BlockingQueuedConnection);
    connect(this, &SerialForm::SignalSerialClose,
            SerialThread::Instance(), &SerialThread::SlotSerialClose, Qt::BlockingQueuedConnection);
    connect(this, &SerialForm::SignalUpdate,
            SerialThread::Instance(), &SerialThread::SlotUpdateFireware);
    connect(this, &SerialForm::SignalUpdateStop,
            SerialThread::Instance(), &SerialThread::SlotUpdateStop);
    connect(this, &SerialForm::SignalUpdateExecute,
            SerialThread::Instance(), &SerialThread::SlotUpdateExecute);
    connect(this, &SerialForm::SignalSerialWriteHex,
            SerialThread::Instance(), &SerialThread::SlotSerialWriteHex, Qt::BlockingQueuedConnection);
    connect(this, &SerialForm::SignalSerialWriteStr,
            SerialThread::Instance(), &SerialThread::SlotSerialWriteStr, Qt::BlockingQueuedConnection);

    connect(SerialThread::Instance(), &SerialThread::SignalUpdateTips,
            this, &SerialForm::SlotUpdateTips);
    connect(SerialThread::Instance(), &SerialThread::SignalSerialLog,
            this, &SerialForm::SlotRecvSerialLog);
    connect(SerialThread::Instance(), &SerialThread::SignalUpdateProgress,
            this, &SerialForm::SlotUpdateProgress);

    ui->updateProgressBar->setAlignment(Qt::AlignHCenter);
    ui->updateProgressBar->setValue(0);

    ui->serialBaudBox->addItem("115200");
    ui->serialBaudBox->addItem("19200");
    ui->serialBaudBox->addItem("9600");
    ui->serialBaudBox->addItem("4800");

    ui->PacketComBox->addItem("1024");
    ui->PacketComBox->addItem("512");
    ui->PacketComBox->addItem("256");
    ui->PacketComBox->addItem("128");
    ui->PacketComBox->addItem("64");
    ui->PacketComBox->setCurrentIndex(0);

    ui->parityBox->addItem(u8"无校验");
    ui->parityBox->addItem(u8"奇校验");
    ui->parityBox->addItem(u8"偶校验");
    ui->parityBox->setCurrentIndex(0);

    RefreshSerialPort();

    //QScreen *screen=QGuiApplication::primaryScreen();
    //QRect mm=screen->availableGeometry() ;
    //this->resize(mm.width()/2 - 20, mm.height() * 3 / 4 - 20);
}

SerialForm::~SerialForm()
{
    delete ui;
}

void SerialForm::RefreshSerialPort()
{
    ui->serialComboBox->clear();
    m_PortName.clear();
    for(auto &dev:QSerialPortInfo::availablePorts())
    {
        QSerialPort com;

        com.setPort(dev);
        if(com.open(QIODevice::ReadWrite)) {
            QString tmp = com.portName();
            m_PortName.append(tmp);

            QString des = dev.description();
            if(!des.isEmpty()) {
                tmp += " ";
                int index = des.indexOf(' ');
                if(index > 0)
                    tmp += des.mid(0, index);
                else
                    tmp += des.left(8);
            }

            ui->serialComboBox->addItem(tmp);
            com.close();
        }
    }
}


void SerialForm::on_serialOperationBtn_clicked()
{
    if(m_SerialIsOpen) {
        ui->serialComboBox->setEnabled(true);
        ui->refreshSerialButton->setEnabled(true);
        emit SignalSerialClose();

        m_SerialIsOpen = false;
        ui->serialOperationBtn->setText(u8"打开串口");
    }
    else {
        QString baud = ui->serialBaudBox->currentText();
        emit SignalSerialOpen(m_PortName.at(ui->serialComboBox->currentIndex()), baud.toUInt(), ui->parityBox->currentIndex());

        //QThread::msleep(200);
        m_SerialIsOpen = SerialThread::Instance()->IsOpen();
        if(m_SerialIsOpen) {
            ui->serialOperationBtn->setText(u8"关闭串口");
            ui->serialComboBox->setEnabled(false);
            ui->refreshSerialButton->setEnabled(false);
        }
    }
}

void SerialForm::on_refreshSerialButton_clicked()
{
    if(m_SerialIsOpen) return;

    emit SignalSerialClose();
    //QThread::msleep(100);

    if(!SerialThread::Instance()->IsOpen()) {
        ui->serialComboBox->clear();
        m_SerialIsOpen = false;
        ui->serialOperationBtn->setText(u8"打开串口");
        RefreshSerialPort();
    }
}

void SerialForm::on_fireFileOpenBtn_clicked()
{
    QString path = QFileDialog::getOpenFileName(nullptr,
                                                u8"升级固件",
                                                "/",
                                                "Bin File(*.bin)");
    if(path.isEmpty()) return;

    ui->fireFileEdit->setText(path);
}

void SerialForm::on_startUpdateBtn_clicked()
{
    QString path = ui->fireFileEdit->text();
    if(path.isEmpty() || !m_SerialIsOpen) return;

    //if(PCPUpdatePtl::Instance()->pcp_is_update()) {
    //    QMessageBox::information(nullptr, "warning", u8"当前升级未完成.");
    //    return;
    //}

    if(!m_UpdateIsRun) {
        QFile file(path);
        if(file.open(QIODevice::ReadOnly)) {
            QDataStream out(&file);

            QByteArray totalFile;
            char tmpBuf[1024];
            while(!out.atEnd())
            {
                int sz = out.readRawData(tmpBuf, 1024);
                QByteArray block(tmpBuf, sz);
                totalFile += block;
            }

            int fileLen = totalFile.length();
            qDebug() << "read fireware length " << fileLen;

            quint16 segment = ui->PacketComBox->currentText().toUShort();

            if(fileLen > 100 && fileLen <= 65536 * segment) {
                int updateLen = fileLen / segment + ((fileLen % segment) > 0);

                ui->updateProgressBar->setRange(0, updateLen);
                ui->serialOperationBtn->setEnabled(false);
                ui->fireFileOpenBtn->setEnabled(false);

                m_UpdateIsRun = true;
                ui->updateStopBtn->setEnabled(false);
                ui->startUpdateBtn->setText(u8"停止升级");

                QString tmp = "update app\r\n";
                SlotRecvSerialLog(tmp);
                emit SignalSerialWriteStr(tmp);
                //QThread::sleep(2);
                emit SignalUpdate(totalFile, segment, !ui->updateCRCRadioBtn->isChecked());
            }
            else {
                QMessageBox::information(nullptr, "warning", u8"固件长度范围(0.1~8MByte)");
            }
        }
    }
    else {
        ui->updateProgressBar->setValue(0);
        ui->serialOperationBtn->setEnabled(true);
        ui->fireFileOpenBtn->setEnabled(true);
        ui->updateStopBtn->setEnabled(false);
        ui->startUpdateBtn->setText(u8"开始升级");
        m_UpdateIsRun = false;

        emit SignalUpdateStop();
    }
}

void SerialForm::SlotUpdateProgress(int index)
{
    ui->updateProgressBar->setValue(index);
}

void SerialForm::SlotSerialError()
{
    ui->updateProgressBar->setValue(0);
    if(m_SerialIsOpen) {
        ui->serialComboBox->setEnabled(true);
        ui->refreshSerialButton->setEnabled(true);
        ui->serialOperationBtn->setEnabled(true);
        ui->fireFileOpenBtn->setEnabled(true);
        ui->updateStopBtn->setEnabled(false);
        m_SerialIsOpen = false;
        m_UpdateIsRun = false;
        ui->serialOperationBtn->setText(u8"打开串口");
        ui->startUpdateBtn->setText(u8"开始升级");
    }
}

void SerialForm::SlotUpdateTips(const QString &tip, quint8 flag)
{
    if(flag > UPDATE_RET_SUCESSED) ui->updateProgressBar->setValue(0);
    ui->serialOperationBtn->setEnabled(true);
    ui->fireFileOpenBtn->setEnabled(true);
    QMessageBox::information(nullptr, "update tips", tip);

    if(flag == UPDATE_RET_SUCESSED) {
        ui->updateStopBtn->setEnabled(true);
    }

    SlotRecvSerialLog(tip);
}

void SerialForm::SlotRecvSerialLog(const QString& data)
{
    QString msg = QString("[%1]%2").arg(MyDATETIMEMS).arg(data);
    ui->serialPlainEdit->appendPlainText(msg);

    if(ui->serialPlainEdit->blockCount() > 10000) {
        ui->serialPlainEdit->clear();
    }
}

void SerialForm::on_clearLogBtn_clicked()
{
    ui->serialPlainEdit->clear();
}

void SerialForm::on_updateStopBtn_clicked()
{
    emit SignalUpdateExecute();
}

void SerialForm::on_resetBtn_clicked()
{
    QString tmp = "reset\r\n";

    emit SignalSerialWriteStr(tmp);
}

int SerialForm::hexStrToChar(char ch)
{
    if(ch >= '0' && ch <= '9') {
        return ch - '0';
    }

    if(ch >= 'a' && ch <= 'f') {
        return ch - 'a' + 10;
    }

    if(ch >= 'A' && ch <= 'F') {
        return ch - 'A' + 10;
    }

    return 0xff;
}

QString SerialForm::byteArrayToHexStr(const QByteArray &data)
{
    QString temp = "";
    QString hex = data.toHex();

    for (int i = 0; i < hex.length(); i = i + 2) {
        temp += hex.mid(i, 2) + " ";
    }

    return temp.trimmed().toUpper();
}

QByteArray SerialForm::hexStrToByteArray(const QString& data)
{
    QByteArray tmp;

    int len = data.length(), i, j;
    tmp.resize(len/2);

    int hhex, lhex;
    for(i = 0, j = 0; i < len;) {
        char hch = data.at(i).toLatin1();
        if(hch == ' ' ) {
            i++;
            continue;
        }
        i++;
        if(i >= len) break;
        hhex = hexStrToChar(hch);

        char lch = data.at(i).toLatin1();
        lhex = hexStrToChar(lch);

        i++;

        //左字符出错 || 右字符出错且不是空格跳过
        if(hhex == 0xff) continue;
        if(lhex == 0xff && lch != ' ') continue;
        //右字符是空格，则左字符按单字符补0处理
        if(lhex != 0xff) {
            hhex = hhex * 16 + lhex;
        }
        tmp[j] = static_cast<char>(hhex);
        j++;
    }

    if(j < len/2) {
        tmp.remove(j, len/2 - j);
    }

    return tmp;
}

bool SerialForm::checkHexStr(QString& input, bool isTips)
{
    input = input.trimmed();
    input.replace(" ", "");

    int len = input.length();
    if(len == 0) return false;

    int i = 0, j = 0;
    for(i = 0; i < len; i++)
    {
        if((input.at(i) >= '0' && input.at(i) <= '9')
        || (input.at(i) >= 'A' && input.at(i) <= 'F')
        || (input.at(i) >= 'a' && input.at(i) <= 'f')) {
            j++;
        }
    }

    if(i != j && isTips) {
        QMessageBox::information(nullptr, "warning", "invalid hex string!");
    }

    return (i == j);
}

void SerialForm::on_crcCheckBtn_clicked()
{
    QString input = ui->crcValEdit->text();

    if(checkHexStr(input, true)) {
        QByteArray data2 = hexStrToByteArray(input);
        quint16 crcval = PCPUpdatePtl::Instance()-> \
                pcp_crc_16(reinterpret_cast<uint8_t*>(data2.data()), data2.length());
        data2.push_back(static_cast<char>(crcval >> 8));
        data2.push_back(static_cast<char>(crcval&0xff));

        QString tmp = byteArrayToHexStr(data2);
        ui->crcValEdit->setText(tmp);
    }
}

void SerialForm::on_serialSendBtn_clicked()
{
    if(!m_SerialIsOpen || PCPUpdatePtl::Instance()->pcp_is_update()) {
        return;
    }

    QString input = ui->serialSendEdit->text();
    if(checkHexStr(input, true)) {
        QByteArray data = hexStrToByteArray(input);
        emit SignalSerialWriteHex(data);
    }
}

void SerialForm::on_serialEnterSendBtn_clicked()
{
    if(!m_SerialIsOpen || PCPUpdatePtl::Instance()->pcp_is_update()) {
        return;
    }

    QString tmp = "\n";
    emit SignalSerialWriteStr(tmp);
}

void SerialForm::on_shellBtn_clicked()
{
    if(!m_SerialIsOpen || PCPUpdatePtl::Instance()->pcp_is_update()) return;

    SerialThread::Instance()->set_shell(true);

    ShellDialog* dlg = new ShellDialog();
    dlg->exec();

    delete dlg;

    SerialThread::Instance()->set_shell(false);
}
